//SocketLibSocket.cpp
 
#include <iostream>
#include <string>
#include <cstring>
#include "SocketLibTypes.h"
#include "SocketLibSocket.h"
#include "SocketLibSystem.h"

namespace SocketLib{


	Socket::Socket(int sock = -1){
		m_sock = sock;
		m_isblocking = true;
		memset(&(m_localinfo), 0, sizeof(m_localinfo));
	}

	int Socket::CreateSock(){
		m_sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
		if(m_sock == -1){
			throw Exception(GetError());
		}
		return m_sock;
	}

	int Socket::GetSock(){
		return m_sock;
	}

	port Socket::GetLocalPort(){
		return ntohs(m_localinfo.sin_port);
	}

	ipaddress Socket::GetLocalAddress(){
		return m_localinfo.sin_addr.s_addr;
	}

	void Socket::SetBlocking(bool p_blockmode){
		int err;
		int flags = fcntl( m_sock, F_GETFL, 0 );
        // set or clear the non-blocking flag
        if( p_blockmode == false ){
            flags |= O_NONBLOCK;
        }
        else{
            flags &= ~O_NONBLOCK;
        }
        err = fcntl( m_sock, F_SETFL, flags );
        if(err == -1){
        	throw(Exception(GetError()));
        }
		m_isblocking = p_blockmode;
	}

	void Socket::Close(){
		if(m_sock != -1){
			close(m_sock);
			m_sock = -1;
		}	
	}

	Socket::~Socket(){

	}




	ListeningSocket::ListeningSocket(){
		m_listening = false;
	}

	ListeningSocket::~ListeningSocket(){

	}

	void ListeningSocket::Listen(port p_port, ipaddress p_ip, int backlog){
		int ret;
		if(m_sock == -1){
			CreateSock();
		}
		// set the SO_REUSEADDR option on the socket, so that it doesn't
        // hog the port after it closes.
		int reuse = 1;
        ret = setsockopt( m_sock, SOL_SOCKET, SO_REUSEADDR, 
                          (char*)(&reuse), sizeof( reuse ) );
        if( ret != 0 )
        {
            throw Exception( GetError() );
        }
		m_localinfo.sin_family = AF_INET;
		m_localinfo.sin_port   = htons(p_port);
		m_localinfo.sin_addr.s_addr = p_ip;
		memset(&(m_localinfo.sin_zero), 0, 8);
		ret = bind(m_sock, (struct sockaddr*)&m_localinfo, sizeof(struct sockaddr_in));
		if(ret == -1){
			throw Exception(GetError());
		}


		//listen的第二个参数并不是用来限制程序的最大连接数的。
		//而是TCP模块允许的已完成三次握手过程(TCP模块完成)但还没来得及被应用程序accept的最大链接数
		ret = listen(m_sock, backlog);
		if(ret ==-1){
			throw Exception(GetError());
		}
	}

	DataSocket ListeningSocket::Accept(){
		int sock = accept(m_sock, NULL, 0);
		if(sock == -1){
			throw Exception(GetError());
		}
		return DataSocket(sock);

	}

	bool ListeningSocket::IsListening(){
		return m_listening;
	}

	void ListeningSocket::Close(){
		Socket::Close();
		m_listening = false;
	}




	DataSocket::DataSocket(int sock)
	:Socket(sock), m_connected(false){
		int err;
		if(sock != -1){
			socklen_t sa_size = sizeof(struct sockaddr_in);
			err = getpeername(sock, (struct sockaddr*)&m_remoteinfo, &sa_size);
			if(err == -1){
				Error e = GetError();
				if(e != ENotConnected){
					throw Exception(e);
				}
			}
			m_connected = true;
		}

	}

	DataSocket::~DataSocket(){

	}

	ipaddress DataSocket::GetRemoteAddress(){
		return m_remoteinfo.sin_addr.s_addr;
	}

	port DataSocket::GetRemotePort(){
		return ntohs(m_remoteinfo.sin_port);
	}

	bool DataSocket::IsConnected(){
		return m_connected;
	}

	void DataSocket::Connect(const char* ipaddress, int port){
		int ret;
		if(m_connected == true){
			throw Exception( EAlreadyConnected );
		}
		if(m_sock == -1){
			CreateSock();
		}
		socklen_t sa_size = sizeof(struct sockaddr_in);
		m_remoteinfo.sin_family = AF_INET;
		m_remoteinfo.sin_port = htons(port);
		m_remoteinfo.sin_addr.s_addr = inet_addr(ipaddress);
		memset(&(m_remoteinfo.sin_zero), 0, 8);

		ret = connect(m_sock, (struct sockaddr*)&m_remoteinfo, sa_size);
		if(ret == -1){
			throw Exception(GetError());
		}
		ret = getsockname( m_sock, (struct sockaddr*)(&m_localinfo), &sa_size);
		if(ret != 0){
			throw Exception(GetError());
		}
		m_connected = true;
	}

 	int DataSocket::Send( const char* p_buffer, int p_size ){
        int err;

        // make sure the socket is connected first.
        if( m_connected == false ){
            throw Exception( ENotConnected );
        }

        // attempt to send the data
        err = send( m_sock, p_buffer, p_size, 0 );
        if( err == -1 ){
            Error e = GetError();
            if( e != EOperationWouldBlock ){
                throw Exception( e );
            }

            // if the socket is nonblocking, we don't want to send a terminal
            // error, so just set the number of bytes sent to 0, assuming
            // that the client will be able to handle that.
            err = 0;
        }

        // return the number of bytes successfully sent
        return err;
    }

	int DataSocket::Receive(char *buff, int len){
		int ret;
		if(m_connected == false){
			throw Exception( ENotConnected );
		}
		ret = recv(m_sock, buff, len, 0);
		if(ret == 0){
			throw Exception( EConnectionClosed );
		}
		if(ret == -1){
			throw Exception( GetError() );
		}
		return ret;
	}

	void DataSocket::Close(){
		if( m_connected == true ){
	        shutdown( m_sock, 2 );
	    }
		Socket::Close();
		m_connected = false;
	}





	SocketSet::SocketSet(){
		FD_ZERO(&m_set);
		FD_ZERO(&m_activityset);
		m_socketdescs.clear();
	}
	
	SocketSet::~SocketSet(){ 

	}

	void SocketSet::AddSocket(Socket& sock){
		FD_SET(sock.GetSock(), &m_set);
		m_socketdescs.insert(sock.GetSock());

	}

	void SocketSet::RemoveSocket(Socket& sock){
		FD_CLR(sock.GetSock(), &m_set);
		m_socketdescs.erase(sock.GetSock());
	}

	int  SocketSet::Poll(long msec){
		if(m_socketdescs.empty()) return 0;

		m_activityset = m_set;
		struct timeval t = {msec/1000, (msec%1000)*1000};
		//把read的fd_set放到write的参数位置会让函数认为fd_set中的socket完成write,并使select返回,造成好像有数据接收的假象
		//*(m_socketdescs.rbegin())+1
		return select(*(m_socketdescs.rbegin())+1, &m_activityset, NULL, NULL, &t);

	}

	bool SocketSet::HasActivity(Socket& sock){
		return FD_ISSET(sock.GetSock(), &m_activityset);
	}


}// end of namespace SocketLib
